#ifndef ASN1_H__
#define ASN1_H__
#include "t1.h"
#include <stdint.h>
#include <string.h>

typedef enum {
    samCommand = 0,
    hfCommand = 1,
    hwioCommand = 6,
    i2cCommand = 10,
    sidecarCommand = 14,
    bleModuleCommand = 21,
    response = 29
} ASN1_Payload_t;

enum Constructed_tag {
    SAMCOMMAND = 0 + 0xA0,
    DETECTED_CARD_INFO = 0 + 0xA0,
    BLEMODULE_RESPONSE_TRANSCEIVE = 2 + 0xA0,
    HWIO_RESPONSE = 4 + 0xA0,
    BLEMODULE_RESPONSE_GET_VERSION2 = 12 + 0xA0,
    SAMCOMMAND_SET_DETECTED_CARD_INFO = 13 + 0xA0,
    SIDECAR_RESPONSE = 14 + 0xA0,
    RESPONSE_BLECOMMAND = 22 + 0xA0,
    PAYLOAD_RESPONSE = 29 + 0xA0,
    SAMCOMMAND_GET_CONTENT_ELEMENT2 = 30 + 0xA0,
};

enum Primitive_tag {
    PROTOCOL = 0 + 0x80,
    BLEMODULE_RESPONSE_TRANSCEIVE_RAPDU = 0 + 0x80,
    BLEMODULE_RESPONSE_GET_VERSION2_VERSION = 0 + 0x80,
    HWIO_COMMAND_ACK = 0 + 0x80,
    CONTENT_ELEMENT_TAG = 0 + 0x80,
    CSN = 1 + 0x80,
    BLEMODULE_RESPONSE_GET_VERSION2_STATIC_RANDOM_ADDRESS = 1 + 0x80,
    BLEMODULE_RESPONSE_GET_VERSION2_PROPERTY_VERSION_LIST = 2 + 0x80,
    SAMCOMMAND_GETVERSIONINFO = 2 + 0x80,
    SIDECAR_RESPONSE_PLATFORM_INFO = 3 + 0x80,
    EXIT_RF_ACTION = 9 + 0x80,
};

#define EXIT_RF_ACTION_NONE {0x00}
#define IMPLICIT_FORMAT_PHYSICAL_ACCESS_BITS {0x04}
#define PROTOCOL_APDU_OVER_BLE {0x00, 0x15}

typedef enum {
    NODE_NULL = 0x00,
    NODE_CORE = 0x01,
    NODE_SAM = 0x0A,
    NODE_HF = 0x14,
    NODE_BLE = 0x2D,
    NODE_COREIO = 0x3E,
    NODE_SIDECAR = 0x42,
    NODE_EXT_APP = 0x44,
    NODE_I2C = 0x54,
} ASN1_Node_id;

typedef union {
    enum Universal_tag { DUMMY_TAG = 0xFF } universal;
    enum Constructed_tag constructed;
    enum Primitive_tag primitive;
} tag_t;

typedef struct asn1_builder_s {
    int currentTag;
    uint8_t payload[255];
    uint8_t tagPtrs[64];
    uint16_t dataPtr;
    struct asn1_builder_s *(*openTag)(struct asn1_builder_s *self, tag_t tag);
    struct asn1_builder_s *(*closeTag)(struct asn1_builder_s *self);
    struct asn1_builder_s *(*closeAllTags)(struct asn1_builder_s *self);
    struct asn1_builder_s *(*create)(struct asn1_builder_s *self, uint8_t value[], uint16_t len);
    struct asn1_builder_s *(*setLength)(struct asn1_builder_s *self, uint16_t length);
} asn1_builder_t;

#define TAG(t) openTag(obj, (tag_t)(enum Universal_tag)t)
#define VALUE(...) create(obj, (uint8_t[])__VA_ARGS__, sizeof((uint8_t[])__VA_ARGS__))
#define FVALUE(apdu, len) create(obj, apdu, len)
#define CLOSE() closeTag(obj)
#define CLOSEALL() closeAllTags(obj)
#define ASN1(o, r)                                                                                 \
    {                                                                                              \
        memset(o.payload, 0, 255);                                                                 \
        memset(o.tagPtrs, 0, 64);                                                                  \
        o.currentTag = 0;                                                                          \
        o.dataPtr = 0;                                                                             \
        o.openTag = asn1_openTag;                                                                  \
        o.create = asn1_create;                                                                    \
        o.closeTag = asn1_closeTag;                                                                \
        o.closeAllTags = asn1_closeAllTags;                                                        \
        o.setLength = asn1_setLength;                                                              \
        asn1_builder_t *obj = &o;                                                                  \
        obj r;                                                                                     \
    }

asn1_builder_t *asn1_setLength(asn1_builder_t *self, uint16_t length);
asn1_builder_t *asn1_openTag(asn1_builder_t *self, tag_t tag);
asn1_builder_t *asn1_closeTag(asn1_builder_t *self);
asn1_builder_t *asn1_closeAllTags(asn1_builder_t *self);
asn1_builder_t *asn1_create(asn1_builder_t *self, uint8_t value[], uint16_t len);

typedef union {
    uint8_t raw;
    struct {
        uint8_t tagid : 5;
        uint8_t type : 1;
        uint8_t classtype : 2;
    };
} asn1_tag_t;

typedef uint16_t asn1_len_t;
typedef uint8_t *asn1_val_t;

typedef enum { SHORT = 0, LONG = 1 } asn1_tlv_len_form_t;

typedef union __attribute__((packed)) {
    uint8_t raw;

    struct __attribute__((packed)) {
        uint8_t len : 7;
        asn1_tlv_len_form_t form : 1;
    };
} asn1_tlv_len_t;

typedef struct {
    asn1_tag_t tag;
    uint16_t len;
    uint8_t *value;
} asn1_tlv_t;

void /*t1_inf_t*/ asn1_i2c_unwrap(t1_inf_t *i2c_apdu, t1_inf_t *dest, /*t1_len_t*/ uint16_t *len);
asn1_tlv_t asn1_unwrap(uint8_t *payload);

#endif /* ASN1_H__ */